﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BeanboxSoftware.BeanMap;
using BeanboxSoftware.BeanMapTest;

namespace BeanMapTest.Unboxed
{
  [TestClass]
  public class ChildMapKVTest
  {
    private Map<int, int, int, int> _parent;
    private IMap<int, int, int> _map;
    private IDictionary<int, IDictionary<int, int>> _dic;

    [TestInitialize]
    public void ChildSetUp()
    {
      _parent = new Map<int, int, int, int>();
      _map = _parent[7];
      _dic = (IDictionary<int, IDictionary<int, int>>)_map;
    }

    [TestMethod]
    public void ChildSetGetRangeTest()
    {
      _map[1, 2] = 3;
      var kvChild = _map[1];
      var v = _map[1, 2];

      Assert.AreEqual(3, kvChild[2]);
      Assert.AreEqual(3, v);
    }

    [TestMethod]
    public void ChildChildSetTest()
    {
      var kvChild = _map[1];
      kvChild[2] = 3;

      Assert.AreEqual(3, _map[1, 2]);
    }

    [TestMethod]
    public void ChildChildGetTest()
    {
      _map[1, 2] = 3;

      var kvChild = _map[1];

      Assert.AreEqual(3, kvChild[2]);
    }

    [TestMethod]
    public void ChildContainsKeyTest()
    {
      Assert.IsFalse(_map.ContainsKey(1));
      Assert.IsFalse(_map.ContainsKey(1, 2));

      _map[1, 2] = 3;

      Assert.IsTrue(_map.ContainsKey(1));
      Assert.IsTrue(_map.ContainsKey(1, 2));
    }

    [TestMethod]
    public void ChildDefaultBehaviorTest()
    {
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1, 2]; });
    }

    [TestMethod]
    public void ChildDefaultValueTest()
    {
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1, 2]; });
      Assert.IsFalse(_map.ContainsKey(1, 2));
      _parent.DefaultValue = -1;
      Assert.AreEqual(KeyNotFoundBehavior.DefaultValue, _parent.KeyNotFoundBehavior);
      Assert.AreEqual(-1, _map[1, 2]);
      Assert.IsFalse(_map.ContainsKey(1, 2));
    }

    [TestMethod]
    public void ChildDefaultGenerationTest()
    {
      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = _map[1, 2]; });
      Assert.IsFalse(_map.ContainsKey(1, 2));
      _parent.DefaultGeneration = (k1, k2, k3) => k2 + k3;
      Assert.AreEqual(KeyNotFoundBehavior.DefaultGeneration, _parent.KeyNotFoundBehavior);
      Assert.AreEqual(3, _map[1, 2]);
      Assert.IsTrue(_map.ContainsKey(1, 2));
    }

    [TestMethod]
    public void ChildChildBehaviorTest()
    {
      var kvChild = _map[1];

      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() => { int i = kvChild[23]; });

      _parent.DefaultGeneration = (k1, k2, k3) => k2 + k3;
      Assert.AreEqual(3, kvChild[2]);
      Assert.IsTrue(_map.ContainsKey(1, 2));
      Assert.IsTrue(kvChild.ContainsKey(2));

      Assert.AreEqual(8, kvChild[7]);
      Assert.IsTrue(_map.ContainsKey(1, 2));
      Assert.IsTrue(kvChild.ContainsKey(7));

      _parent.DefaultValue = -1;
      Assert.AreEqual(-1, kvChild[4]);
      Assert.IsFalse(_map.ContainsKey(1, 4));
      Assert.IsTrue(kvChild.ContainsKey(2));
      Assert.IsFalse(kvChild.ContainsKey(4));
    }

    [TestMethod]
    public void ChildCountTest()
    {
      Assert.AreEqual(0, _map.Count);
      _map[1, 2] = 3;
      Assert.AreEqual(1, _map.Count);
      _map[1, 3] = 4;
      Assert.AreEqual(2, _map.Count);
    }

    [TestMethod]
    public void ChildTryGetTest()
    {
      IDictionary<int, int> child;
      Assert.IsTrue(_map.TryGetValue(14, out child));
      Assert.IsNotNull(child);

      child[0] = 0;
      Assert.AreEqual(0, _map[14, 0]);
      _parent.DefaultValue = -3;
      Assert.AreEqual(-3, child[1]);
      Assert.IsFalse(child.ContainsKey(1));
      Assert.IsFalse(_map.ContainsKey(14, 1));
    }

    [TestMethod]
    public void ChildInternalGetTest()
    {
      var dic = (IDictionary<int, IDictionary<int, int>>)_map;

      var child = dic[1];
      Assert.IsNotNull(child);
      Assert.IsFalse(_map.ContainsKey(1));
      Assert.IsFalse(_map.ContainsKey(1, 2));
      child[2] = 4;
      Assert.AreEqual(4, _map[1, 2]);
      Assert.IsTrue(_map.ContainsKey(1, 2));
    }

    [TestMethod]
    public void ChildInternalSetTest()
    {
      var dic = (IDictionary<int, IDictionary<int, int>>)_map;
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { dic[1] = new Dictionary<int, int>(); });
    }

    [TestMethod]
    public void ChildIsDiciontaryTest()
    {
      Assert.IsNotNull(_dic);
    }

    [TestMethod]
    public void ChildIsReadOnlyTest()
    {
      //as a collection, it _is_ read only
      Assert.IsFalse(_dic.IsReadOnly);
    }

    [TestMethod]
    public void ChildKeysTest()
    {
      Assert.AreEqual(0, _dic.Keys.Count);

      _map[1, 2] = 4;
      Assert.AreEqual(1, _dic.Keys.Count);
      Assert.AreEqual(1, _dic.Keys.First());

      _map.Clear();
      _map[2, 3] = 1;
      Assert.AreEqual(1, _dic.Keys.Count);
      Assert.AreEqual(2, _dic.Keys.First());
    }

    [TestMethod]
    public void ChildForEachTest()
    {
      foreach (var kvp in _dic)
      {
        Assert.Fail("unexpected kvp");
      }

      _map[1, 2] = 4;

      foreach (var kvp in _dic)
      {
        Assert.AreEqual(1, kvp.Key);
        Assert.AreEqual(4, kvp.Value[2]);
      }
    }

    [TestMethod]
    public void ChildForEachSecurityTest()
    {
      _map[1, 2] = 4;

      Assert.AreEqual(4, _dic.First().Value[2]);
      _dic.First().Value[2] = 5;
      Assert.AreEqual(5, _map[1, 2]);

      _dic.First().Value[4] = 8;
      Assert.AreEqual(8, _map[1, 4]);

      _parent.DefaultValue = -1;
      Assert.AreEqual(-1, _dic.First().Value[5]);
    }

    [TestMethod]
    public void ChildAddNotSupportedTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Add(1, new Dictionary<int, int>()); });
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Add(new KeyValuePair<int, IDictionary<int, int>>()); });
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic[1] = new Dictionary<int, int>(); });
    }

    [TestMethod]
    public void ChildGetTest()
    {
      _map[1, 2] = 4;
      Assert.AreEqual(4, _dic[1][2]);
      _dic[1][2] = 5;
      Assert.AreEqual(5, _dic[1][2]);
      Assert.AreEqual(5, _map[1, 2]);
    }

    [TestMethod]
    public void ChildContainsKVPTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Contains(new KeyValuePair<int, IDictionary<int, int>>()); });
    }

    [TestMethod]
    public void ChildCopyToTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, int>>[4];
      _map[1, 2] = 4;
      _map[2, 2] = 2;
      _map[3, 9] = 3;

      _dic.CopyTo(buffer, 1);

      var one = buffer.First(e => e.Key == 1);
      var two = buffer.First(e => e.Key == 2);
      var three = buffer.First(e => e.Key == 3);

      Assert.AreEqual(4, one.Value[2]);
      Assert.AreEqual(2, two.Value[2]);
      Assert.AreEqual(3, three.Value[9]);
    }

    [TestMethod]
    public void ChildCopyToNullTest()
    {
      TestUtils.AssertExceptionThrown<ArgumentNullException>(() => { _dic.CopyTo(null, 0); });
    }

    [TestMethod]
    public void ChildCopyToOutOfRangeTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, int>>[4];
      TestUtils.AssertExceptionThrown<ArgumentOutOfRangeException>(() => { _dic.CopyTo(buffer, -1); });
      TestUtils.AssertExceptionThrown<ArgumentException>(() => { _dic.CopyTo(buffer, buffer.Length + 1); });
      TestUtils.AssertExceptionThrown<ArgumentException>(() => { _dic.CopyTo(buffer, buffer.Length + 100); });

      _map[1, 1] = 4;
      _map[2, 2] = 6;
      TestUtils.AssertExceptionThrown<ArgumentException>(() => { _dic.CopyTo(buffer, buffer.Length - 1); });

      _map.Clear(); // no values should be okay
      _dic.CopyTo(buffer, buffer.Length);

      _map[1, 1] = 1;
      _map[1, 2] = 2;
      _map[2, 2] = 3;
      _map[2, 3] = 4; // only 2 to copy
      _dic.CopyTo(buffer, buffer.Length - 2);
      Assert.AreEqual(4, buffer.First(e => e.Key == 2 && e.Value.ContainsKey(3)).Value[3]);
    }

    [TestMethod]
    public void ChildCopyToSecurityTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, int>>[3];
      _map[1, 2] = 4;
      _map[2, 2] = 2;

      _dic.CopyTo(buffer, 1);

      var one = buffer.First(e => e.Key == 1);
      var two = buffer.First(e => e.Key == 2);

      _parent.DefaultValue = -1;
      Assert.AreEqual(-1, two.Value[909]);
    }

    [TestMethod]
    public void ChildRemoveKVPTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Remove(new KeyValuePair<int, IDictionary<int, int>>()); });
    }

    [TestMethod]
    public void ChildValuesTest()
    {
      Assert.AreEqual(0, _dic.Count);

      _map[1, 2] = 7;
      _map[2, 2] = 7;
      _map[3, 2] = 7;
      _map[4, 2] = 7;

      var values = _dic.Values;
      Assert.AreEqual(4, values.Count);

      foreach (var v in values)
      {
        Assert.AreEqual(7, v[2]);
      }
    }

    [TestMethod]
    public void ChildRemoveTest()
    {
      Assert.IsFalse(_map.Remove(1));
      Assert.IsFalse(_map.Remove(1, 2));
      Assert.IsFalse(_parent.Remove(7, 1, 2));

      _map[1, 2] = 3;
      Assert.IsTrue(_parent.ContainsKey(7, 1, 2));
      Assert.IsTrue(_map.Remove(1, 2));
      Assert.AreEqual(0, _map.Count);
      Assert.AreEqual(0, _parent.Count);
      Assert.IsFalse(_map.ContainsKey(1));
      Assert.IsFalse(_map.ContainsKey(1, 2));
      Assert.IsFalse(_parent.ContainsKey(7, 1, 2));

      _map[1, 2] = 3;
      Assert.IsTrue(_map.Remove(1));
      Assert.AreEqual(0, _map.Count);
      Assert.AreEqual(0, _parent.Count);
      Assert.IsFalse(_map.ContainsKey(1));
      Assert.IsFalse(_map.ContainsKey(1, 2));
      Assert.IsFalse(_parent.ContainsKey(7, 1, 2));

      _map[1, 1] = 4;
      _map[1, 2] = 4;
      Assert.AreEqual(2, _map.Count);
      Assert.AreEqual(2, _parent.Count);
      Assert.IsTrue(_map.ContainsKey(1, 1));
      Assert.IsTrue(_map.ContainsKey(1, 2));
      
      Assert.IsTrue(_parent.ContainsKey(7, 1, 2));
      Assert.IsTrue(_parent.ContainsKey(7, 1, 2));

      Assert.IsTrue(_map.Remove(1, 1));
      Assert.AreEqual(1, _map.Count);
      Assert.AreEqual(1, _parent.Count);

      Assert.IsFalse(_map.ContainsKey(1, 1));
      Assert.IsTrue(_map.ContainsKey(1, 2));

      Assert.IsFalse(_parent.ContainsKey(7, 1, 1));
      Assert.IsTrue(_parent.ContainsKey(7, 1, 2));

      Assert.IsTrue(_map.Remove(1));
      Assert.AreEqual(0, _map.Count);
      Assert.AreEqual(0, _parent.Count);
    }

    [TestMethod]
    public void ChildChildRemoveTest()
    {
      var child = _map[14];
      Assert.AreEqual(0, _parent.Count);
      Assert.AreEqual(0, _map.Count);
      Assert.AreEqual(0, child.Count);

      Assert.IsFalse(_parent.ContainsKey(7));
      Assert.IsFalse(_parent.ContainsKey(7, 14));
      Assert.IsFalse(_map.ContainsKey(14));

      child[21] = -89;

      Assert.AreEqual(1, _parent.Count);
      Assert.AreEqual(1, _map.Count);
      Assert.AreEqual(1, child.Count);

      Assert.IsTrue(_parent.ContainsKey(7));
      Assert.IsTrue(_parent.ContainsKey(7, 14));
      Assert.IsTrue(_parent.ContainsKey(7, 14, 21));
      Assert.AreEqual(-89, _parent[7, 14, 21]);

      Assert.IsTrue(_map.ContainsKey(14));
      Assert.IsTrue(_map.ContainsKey(14, 21));
      Assert.AreEqual(-89, _map[14, 21]);

      Assert.IsTrue(child.ContainsKey(21));
      Assert.AreEqual(-89, child[21]);

      child.Remove(21);
      Assert.AreEqual(0, _parent.Count);
      Assert.AreEqual(0, _map.Count);
      Assert.AreEqual(0, child.Count);

      Assert.IsFalse(_parent.ContainsKey(7));
      Assert.IsFalse(_parent.ContainsKey(7, 14));
      Assert.IsFalse(_parent.ContainsKey(7, 14, 21));
      
      Assert.IsFalse(_map.ContainsKey(14));
      Assert.IsFalse(_map.ContainsKey(14, 21));

      Assert.IsFalse(child.ContainsKey(21));
    }
  }
}
